import { Mat4 } from "./Mat4";
import { Vec3 } from "./Vec3";
import { ANGLE_ORDER, ArrayType, EPSILON,clamp } from "./common";
/**
 * 三维空间的欧拉角对象，单位:弧度。
 * @class
 * @author jsbrml
 */
class Euler extends ArrayType {
   /**
     * 构造函数。
     * @constructor
     * @param {Array|Vec3} element 【可填】缺省值[0,0,0],x轴y轴z轴的旋转方向,单位弧度。
     * @param {String} order 【可填】缺省值"zyx",旋转顺序。
     * @example
     * let euler=new Euler([Math.PI/2,Math.PI/3,Math.PI/4],"xyz");
     */
    constructor(element = [0, 0, 0], order = ANGLE_ORDER) {
        super(element);
        this._order = order;
    }
    set order(v) {
        this._order = v;
        return this;
    }
    /**
     * @property  {String} order 
     * 【读写】欧拉角的旋转方向。可选项:"xyz"|"xzy"|"yzx"|"yxz"|"zxy"|"zyx"
     */
    get order() {
        return this._order;
    }
    /**
     * @property  {Number} x 
     * 【读写】x轴旋转分量,单位弧度。
     */
    get x() {
        return this[0];
    }
    set x(v) {
        this[0] = v;
    }
    /**
     * @property  {Number} y 
     * 【读写】y轴旋转分量,单位弧度。
     */
    get y() {
        return this[1];
    }
    set y(v) {
        this[1] = v;
    }
    /**
     * @property  {Number} z
     * 【读写】z轴旋转分量,单位弧度。
     */
    get z() {
        return this[2];
    }
    set z(v) {
        this[2] = v;
    }
    /**
     * Euler的复制方法。
     * @method
     * @public
     * @param {Euler|Vec3|Array} v 源对象。
     * @example
     * let euler=new Euler([Math.PI/2,Math.PI/3,Math.PI/4],"xyz");
     * let copy=new Euler().copy(euler);
     * @returns {Euler} 欧拉角。
     */
    copy(v) {
        this[0] = v[0];
        this[1] = v[1];
        this[2] = v[2];
        this.order=v.order?v.order:ANGLE_ORDER;
        return this;
    }
    /**
     * Euler的克隆方法。
     * @method
     * @public
     * @example
     * let euler=new Euler([Math.PI/2,Math.PI/3,Math.PI/4],"xyz");
     * let copy=euler.clone();
     * @returns {Euler} 当前对象的克隆。
     */
    clone() {
        return new Euler(this);
    }
    /**
     * 判断两个Euler是否相等。
     * @method
     * @public
     * @example
     * let euler=new Euler([Math.PI/2,Math.PI/3,Math.PI/4],"xyz");
     * let euler1=new Euler([Math.PI/2,Math.PI/3,Math.PI/4],"xyz");
     * let isEqual=euler.equal(euler1);
     * @returns {Euler} 当前对象的克隆。
     */
    equal(v) {
        return (Math.abs(this[0] - v[0]) <= EPSILON) && (Math.abs(this[1] - v[1]) <= EPSILON) && (Math.abs(this[2] - v[2]) <= EPSILON);
    }
    /**
     * Euler转为字符串。
     * @method
     * @public
     * @example
     * let euler=new Euler([Math.PI/2,Math.PI/3,Math.PI/4],"xyz");
     * let eulerString=euler.toString();
     * @returns {String} 当前对象的字符串形式。
     */
    toString() {
        return `Euler(${this.join(',')})`
    }
    /**
     * Euler初始化,所有旋转角度都归0。
     * @method
     * @public
     * @example
     * let euler=new Euler([Math.PI/2,Math.PI/3,Math.PI/4],"xyz");
     * euler.identity();//[0,0,0]
     * @returns {Euler} 归0后的Euler。
     */
    identity() {
        this[0] = 0;
        this[1] = 0;
        this[2] = 0;
    }
    /**
     * 从四元素中构成欧拉角。
     * @method
     * @public
     * @example
     * let euler=new Euler();
     * let quat=new Quat([0.4,0.4,0.4,1.0]);
     * euler.fromQuat(quat);
     * @returns {Euler} Euler。
     */
    fromQuat(quat) {
        quat.normalize();
        let mat4 = new Mat4();
        mat4.fromQuat(quat);
        const m11 = mat4[0], m12 = mat4[4], m13 = mat4[8];
        const m21 = mat4[1], m22 = mat4[5], m23 = mat4[9];
        const m31 = mat4[2], m32 = mat4[6], m33 = mat4[10];
        switch (this.order) {
            case 'xyz':
                this[1] = Math.asin(clamp(m13, - 1, 1));

                if (Math.abs(m13) < 0.9999999) {

                    this[0] = Math.atan2(- m23, m33);
                    this[2] = Math.atan2(- m12, m11);

                } else {

                    this[0] = Math.atan2(m32, m22);
                    this[2] = 0;

                }

                break;

            case 'yxz':

                this[0] = Math.asin(- clamp(m23, - 1, 1));

                if (Math.abs(m23) < 0.9999999) {

                    this[1] = Math.atan2(m13, m33);
                    this[2] = Math.atan2(m21, m22);

                } else {

                    this[1] = Math.atan2(- m31, m11);
                    this[2] = 0;

                }

                break;

            case 'zxy':

                this[0] = Math.asin(clamp(m32, - 1, 1));

                if (Math.abs(m32) < 0.9999999) {

                    this[1] = Math.atan2(- m31, m33);
                    this[2] = Math.atan2(- m12, m22);

                } else {

                    this[1] = 0;
                    this[2] = Math.atan2(m21, m11);

                }

                break;

            case 'zyx':

                this[1] = Math.asin(- clamp(m31, - 1, 1));

                if (Math.abs(m31) < 0.9999999) {

                    this[0] = Math.atan2(m32, m33);
                    this[2] = Math.atan2(m21, m11);

                } else {

                    this[0] = 0;
                    this[2] = Math.atan2(- m12, m22);

                }

                break;

            case 'yzx':

                this[2] = Math.asin(clamp(m21, - 1, 1));

                if (Math.abs(m21) < 0.9999999) {

                    this[0] = Math.atan2(- m23, m22);
                    this[1] = Math.atan2(- m31, m11);

                } else {

                    this[0] = 0;
                    this[1] = Math.atan2(m13, m33);

                }

                break;

            case 'xzy':

                this[2] = Math.asin(- clamp(m12, - 1, 1));

                if (Math.abs(m12) < 0.9999999) {

                    this[0] = Math.atan2(m32, m22);
                    this[1] = Math.atan2(m13, m11);

                } else {

                    this[0] = Math.atan2(- m23, m33);
                    this[1] = 0;

                }

                break;

            default:

                console.warn('THREE.Euler: .setFromRotationMatrix() encountered an unknown order: ' + order);

        }
        return this;
    }
}
export { Euler }